using System;
using System.Text;
using System.Collections.Generic;
using Hashes;

namespace Hashes.StringHashes
{
    public class X31 : IHashProvider<string>
    {
        public uint GetHashCode(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                uint hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)                
                    hash = ((hash << 5) - hash + (uint)stringToHash[i]);                    
                
                return hash;
            }
        }

        public ulong GetHashCodeLong(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                ulong hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)
                    hash = ((hash << 11) | (hash >> 53)) - hash + (ulong)stringToHash[i];

                return hash;
            }
        }
    }

    public class TCL : IHashProvider<string>
    {
        public uint GetHashCode(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                uint hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)
                    hash += (hash << 3) + (uint)stringToHash[i];

                return hash;
            }
        }

        public ulong GetHashCodeLong(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                ulong hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)
                    hash += (hash << 6) + (ulong)stringToHash[i];

                return hash;
            }
        }
    }

    public class DJB2 : IHashProvider<string>
    {
        public uint GetHashCode(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                uint hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)
                    hash = ((hash << 5) + hash ^ (uint)stringToHash[i]);

                return hash;
            }
        }

        public ulong GetHashCodeLong(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                ulong hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)
                    hash = ((hash << 11) | (hash >> 53)) + hash + (ulong)stringToHash[i];

                return hash;
            }
        }
    }

    public class SDBM : IHashProvider<string>
    {
        public uint GetHashCode(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                uint hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)
                    hash = ((hash << 6) + (hash << 16) - hash + (uint)stringToHash[i]);

                return hash;
            }
        }

        public ulong GetHashCodeLong(string stringToHash)
        {
            if (stringToHash.Length == 0)
                return 0;
            else
            {
                ulong hash = 0;
                for (int i = 0; i < stringToHash.Length; i++)
                    hash = ((hash << 5) + ((hash << 16) | (hash >> 48)) - hash + (uint)stringToHash[i]);

                return hash;
            }
        }
    }

    public class AshishHash : IHashProvider<string>
    {
        public uint GetHashCode(string stringToHash)
        {
            if (string.IsNullOrEmpty(stringToHash))
                return 0;
            uint hash = 0;
            
            for (int i = 0; i < stringToHash.Length; i++)
            {
                hash = (hash << 5) - hash + _NumTable[(byte) stringToHash[i]] + (uint) stringToHash[i]; 
            }
            return hash;
        }

        public ulong GetHashCodeLong(string stringToHash)
        {
            if (string.IsNullOrEmpty(stringToHash))
                return 0;
            ulong hash = 0;
            for (int i = 0; i < stringToHash.Length; i++)
            {
                hash = (hash << 5) - hash + _NumTable[(byte)stringToHash[i]] + (uint) stringToHash[i];
            }
            return hash;
        }
     
        private static uint[] _NumTable = new uint[]
                                            {
                                                2147548159, 2155571437, 2161335419, 2167099901, 2178857297, 2189305763, 2194624637, 2199943931, 
                                                2208385759, 2216828863, 2221257527, 2228556703, 2237417359, 2249148887, 2258873257, 2268597883, 
                                                2279883733, 2289860863, 2297579183, 2308419941, 2314131481, 2322965591, 2331799693, 2343504323, 
                                                2355208927, 2365607903, 2370402533, 2378068489, 2386850111, 2392065967, 2397252817, 2409130649, 
                                                2421009007, 2432189567, 2438492893, 2446357489, 2454222143, 2464957277, 2475693053, 2485983127, 
                                                2491143937, 2499427369, 2510581217, 2520426023, 2530271327, 2538109603, 2548826429, 2557449239, 
                                                2568942613, 2573997299, 2579052163, 2585668381, 2595155989, 2605759019, 2616362321, 2620528091, 
                                                2628679693, 2633262851, 2640716567, 2646861377, 2655876673, 2666455073, 2673882317, 2680012817, 
                                                2685556733, 2695503341, 2707011341, 2718520823, 2730447883, 2741676937, 2752909823, 2760351871, 
                                                2770664329, 2775847801, 2787024067, 2796891553, 2808320369, 2814619747, 2823789457, 2831650153, 
                                                2842381327, 2851803683, 2858966729, 2866129793, 2871984191, 2882273599, 2892088039, 2896773613, 
                                                2901013513, 2906814791, 2912616559, 2924413103, 2931753017, 2940790823, 2950082563, 2954661803, 
                                                2965932479, 2973634871, 2982898769, 2988594751, 2998722611, 3003974111, 3014525053, 3018579973, 
                                                3025505341, 3031122043, 3036040961, 3041212201, 3049260023, 3059229143, 3070760159, 3080284201, 
                                                3086240009, 3095066627, 3107015797, 3118965001, 3125784721, 3131295767, 3136109569, 3140478977, 
                                                3151958021, 3162292997, 3171320059, 3179216917, 3185380361, 3195977737, 3200122903, 3204766001, 
                                                3215150083, 3225786847, 3234416599, 3240787157, 3245848841, 3253780861, 3261713069, 3271206661, 
                                                3275571323, 3287490607, 3297403643, 3301741271, 3312770089, 3318669451, 3327691447, 3339584239, 
                                                3347908561, 3356236783, 3365206441, 3373478111, 3383311073, 3391835081, 3400360079, 3406179943, 
                                                3410690749, 3418072081, 3428323807, 3437266543, 3449079979, 3457325167, 3467131919, 3478501381, 
                                                3488989187, 3493088983, 3503880389, 3515535479, 3522313241, 3530652353, 3538991777, 3543762821, 
                                                3551404273, 3559045753, 3565378457, 3569451871, 3573530627, 3583797337, 3590495717, 3600064733, 
                                                3611197523, 3617424673, 3628084703, 3636292613, 3640492103, 3649483819, 3661346833, 3669196907, 
                                                3673285153, 3680243863, 3690073633, 3696587201, 3705971107, 3714056447, 3722863633, 3729663761, 
                                                3739334689, 3744129073, 3751348253, 3762998969, 3773340719, 3783682733, 3790457347, 3796785961, 
                                                3804676207, 3810307211, 3814629451, 3823383637, 3828569419, 3840446257, 3852323089, 3864200027, 
                                                3870949559, 3879231883, 3887515777, 3897778363, 3909602453, 3916300471, 3922078253, 3930726901, 
                                                3935807509, 3946881151, 3951516169, 3959029783, 3963559981, 3974946949, 3986587699, 3995077501, 
                                                4006445101, 4010842397, 4018143487, 4029646531, 4036020029, 4042393513, 4048767031, 4058011019, 
                                                4065946123, 4075442821, 4081371253, 4088861713, 4100085973, 4104872021, 4115652613, 4126851089, 
                                                4136294917, 4140611617, 4146016261, 4155154691, 4165854529, 4175245337, 4179506693, 4185329699, 
                                                4195584457, 4207411423, 4215529733, 4223174147, 4230818569, 4241336833, 4252500023, 4257613573
                                            };
    }

    public class JenkinHash3 : IHashProvider<string>
    {
        public uint GetHashCode(string stringToHash)
        {
            if (string.IsNullOrEmpty(stringToHash))
                return 0;

            uint a = 0,b = a;
            HashCore(stringToHash, out a, out b);
            return a;
        }

        public ulong GetHashCodeLong(string stringToHash)
        {
            if (string.IsNullOrEmpty(stringToHash))
                return 0;

            uint a = 0, b = a;
            HashCore(stringToHash, out a, out b);
            return a + ((ulong)b << 32);
        }

        private static void HashCore(string stringToHash, out uint resA, out uint resB)
        {            
            Byte[] bytes = ASCIIEncoding.ASCII.GetBytes(stringToHash);
            uint a = (uint) (0xdeadbeef + (bytes.Length)), b = a,c = a;
            int length = bytes.Length, ptr = 0;
            
            while (length > 12)
            {
                a += BitConverter.ToUInt32(bytes, (ptr << 3) + (ptr << 2));
                b += BitConverter.ToUInt32(bytes, (ptr << 3) + (ptr << 2) + 4);
                c += BitConverter.ToUInt32(bytes, (ptr << 3) + (ptr << 2) + 8);
                Mix(ref a, ref b, ref c);
                length -= 12;
                ptr++;
            }
            ptr = (ptr << 3) + (ptr << 2);
            switch (length)
            {
                case 12: c += BitConverter.ToUInt32(bytes, ptr + 8); b += BitConverter.ToUInt32(bytes, ptr + 4); a += BitConverter.ToUInt32(bytes, ptr); break;
                case 11: c += (((uint)bytes[ptr + 10]) << 16); goto case 10;
                case 10: c += (((uint)bytes[ptr + 9]) << 8); goto case 9;
                case 9: c += bytes[ptr + 8]; goto case 8;
                case 8: b += BitConverter.ToUInt32(bytes, ptr + 4); a += BitConverter.ToUInt32(bytes, ptr); break;
                case 7: b += (((uint)bytes[ptr + 6]) << 16); goto case 6;
                case 6: b += (((uint)bytes[ptr + 5]) << 8); goto case 5;
                case 5: b += bytes[ptr + 4]; goto case 4;
                case 4: a += BitConverter.ToUInt32(bytes, ptr); break;
                case 3: a += (((uint)bytes[ptr + 2]) << 16); goto case 2;
                case 2: a += (((uint)bytes[ptr + 1]) << 8); goto case 1;
                case 1: a += bytes[ptr]; break;
                case 0: resA = c; resB = b; return;
            }
            Final(ref a, ref b, ref c);
            resA = c;
            resB = b;
        }

        private static void Mix(ref uint a, ref uint b, ref uint c)
        {
            a -= c;  a ^= rot(c, 4);  c += b;
            b -= a;  b ^= rot(a, 6);  a += c;
            c -= b;  c ^= rot(b, 8);  b += a;
            a -= c;  a ^= rot(c,16);  c += b;
            b -= a;  b ^= rot(a,19);  a += c;
            c -= b;  c ^= rot(b, 4);  b += a;
        }

        private static void Final(ref uint a, ref uint b, ref uint c)
        {
            c ^= b; c -= rot(b,14);
            a ^= c; a -= rot(c,11);
            b ^= a; b -= rot(a,25);
            c ^= b; c -= rot(b,16);
            a ^= c; a -= rot(c,4); 
            b ^= a; b -= rot(a,14);
            c ^= b; c -= rot(b,24);
        }
        
        private static uint rot(uint a, int rotBy)
        {
            return (a << rotBy) | (a >> (32 - rotBy));
        }
    }

    public class DefaultTwinHashProvider : ITwinHashCodeProvider<string>
    {
        #region ITwinHashCodeProvider Members

        public void GetHashCodes(string stringToHash, out uint hashCodeA, out uint hashCodeB)
        {
            if (string.IsNullOrEmpty(stringToHash))
            {
                hashCodeA = 0;
                hashCodeB = 0;
            }

            uint hashA = 0, hashB = 0;
            for (int i = 0; i < stringToHash.Length; i++)
            {
                hashA = ((hashA << 5) + hashA ^ (uint)stringToHash[i]);
                hashB = ((hashB << 6) + (hashB << 16) - hashB + (uint)stringToHash[i]);
            }
            hashCodeA = hashA;
            hashCodeB = hashB;
        }

        #endregion
    }
}
